
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <sys/ioctl.h>
#include <netdb.h>

#include "hi_ext_util.h"
#include "hi_appcomm.h"
#include "plug_discovery.h"
#include "plug_common.h"
#include "plug_comm_gesture.h"
#include "cJSON.h"
#include "plug_media_recordmng.h"
#include "plug_media_photomng.h"
#include "plug_hisignalling.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */

static HI_BOOL s_bCommGestureRun = HI_FALSE;
static HI_BOOL s_bSocketConnected = HI_FALSE;
static HI_S32 s_s32ConnectFd = -1;

typedef struct PLUG_COMM_GESTURE_SEND_CFG_s
{
    HI_CHAR *paData;
    HI_S32 s32Len;
}PLUG_COMM_GESTURE_SEND_CFG_S;

static HI_VOID PLUG_COMM_GESTURE_TakePhoto()
{
    HI_S32 s32Ret = HI_SUCCESS;
    PLUG_MEDIA_PHOTOMNG_INITCFG_S stInitCfg;
    s32Ret = PLUG_MEDIA_PHOTOMNG_Init(&stInitCfg);
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_MEDIA_PHOTOMNG_Init failed\n");
        return ;
    }

    s32Ret = PLUG_COMMON_CheckFolderExist(PLUG_COMMON_PHOTO_FOLDER);
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_COMMON_CheckFolderExist failed\n");
        return ;
    }

    PLUG_MEDIA_PHOTOMNG_CFG_S stPhotoCfg;
    time_t time_seconds = time(HI_NULL);
    struct tm stNowTime;
    localtime_r(&time_seconds, &stNowTime);

    sprintf(stPhotoCfg.azFilePath, "%s/%04d%02d%02d_%02d%02d%02d.jpg",
        PLUG_COMMON_PHOTO_FOLDER,
        stNowTime.tm_year + 1900, stNowTime.tm_mon + 1, stNowTime.tm_mday, 
        stNowTime.tm_hour, stNowTime.tm_min, stNowTime.tm_sec);
    s32Ret = PLUG_MEDIA_PHOTOMNG_TakePhoto(&stPhotoCfg);
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_MEDIA_PHOTOMNG_TakePhoto failed\n");
    }

    s32Ret = PLUG_MEDIA_PHOTOMNG_Deinit();
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_MEDIA_PHOTOMNG_Deinit failed\n");
    }

    return ;
}

static HI_VOID PLUG_COMM_GESTURE_StartRecord()
{
    HI_S32 s32Ret = HI_SUCCESS;
    PLUG_MEDIA_RECORDMNG_INITCFG_S stInitCfg;
    s32Ret = PLUG_MEDIA_RECORDMNG_Init(&stInitCfg);
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_MEDIA_RECORDMNG_Init failed\n");
        return ;
    }

    s32Ret = PLUG_COMMON_CheckFolderExist(PLUG_COMMON_VIDEO_FOLDER);
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_COMMON_CheckFolderExist failed\n");
        return ;
    }

    PLUG_MEDIA_RECORDMNG_CFG_S stRecordCfg;
    time_t time_seconds = time(HI_NULL);
    struct tm stNowTime;
    localtime_r(&time_seconds, &stNowTime);

    sprintf(stRecordCfg.azFilePath, "%s/%04d%02d%02d_%02d%02d%02d.mp4",
        PLUG_COMMON_VIDEO_FOLDER,
        stNowTime.tm_year + 1900, stNowTime.tm_mon + 1, stNowTime.tm_mday, 
        stNowTime.tm_hour, stNowTime.tm_min, stNowTime.tm_sec);
    s32Ret = PLUG_MEDIA_RECORDMNG_StartRecord(&stRecordCfg);
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_MEDIA_RECORDMNG_StartRecord failed\n");
    }

    return ;
}

static HI_VOID PLUG_COMM_GESTURE_StopRecord()
{
    HI_S32 s32Ret = HI_SUCCESS;

    s32Ret = PLUG_MEDIA_RECORDMNG_StopRecord();
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_MEDIA_RECORDMNG_StopRecord failed\n");
    }

    s32Ret = PLUG_MEDIA_RECORDMNG_Deinit();
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_MEDIA_RECORDMNG_Deinit failed\n");
    }

    return ;
}


static HI_VOID PLUG_COMM_GESTURE_LightOn()
{
    PLUG_HISIGNALLING_SendMsg(PLUG_COMMON_GREEN_LIGHT_ON);
    return ;
}

static HI_VOID PLUG_COMM_GESTURE_LightOff()
{
    PLUG_HISIGNALLING_SendMsg(PLUG_COMMON_GREEN_LIGHT_OFF);
    return ;
}

HI_VOID *PLUG_COMM_GESTURE_SocketSendThread(HI_VOID* args)
{
    pthread_detach(pthread_self());
    PLUG_COMM_GESTURE_SEND_CFG_S *pstSendCfg = (PLUG_COMM_GESTURE_SEND_CFG_S*)args;

    if (NULL == pstSendCfg)
    {
        LOGI("input NULL\n");
        goto exit0;
    }

    HI_S32 s32DataLen = 0;
    HI_S32 s32Ret = HI_SUCCESS;
    HI_S32 s32SendedLen = 0;
    HI_CHAR *pazSendBuff = pstSendCfg->paData;
    if (NULL == pazSendBuff)
    {
        LOGI("input NULL\n");
        goto exit0;
    }

    if (HI_FALSE == s_bCommGestureRun || -1 == s_s32ConnectFd)
    {
        LOGE("Unconnected\n");
        goto exit1;
    }
    
    s32DataLen = pstSendCfg->s32Len;
    while(s32SendedLen < s32DataLen)
    {
        HI_S32 s32SendLen = 0;
        if(s32DataLen - s32SendedLen < 65000)
        {
            s32SendLen = s32DataLen - s32SendedLen;
        }
        else
        {
            s32SendLen = 65000;
        }

        s32Ret = send(s_s32ConnectFd, pazSendBuff + s32SendedLen, s32SendLen, 0);
        if (s32Ret < 0)
        {
            LOGE("send failed, ret = %d\n" , s32Ret);
            break;
        }
        s32SendedLen += s32SendLen;
        s32SendLen = 0;
    }

exit1:
    free(pazSendBuff);
    free(pstSendCfg);
exit0:
    pthread_exit(0);
}

static HI_S32 PLUG_COMM_GESTURE_SocketSend(HI_CHAR *pazData, HI_S32 s32DataLen)
{
    HI_APPCOMM_CHECK_POINTER(pazData, HI_FAILURE);
    if (HI_FALSE == s_bCommGestureRun || -1 == s_s32ConnectFd)
    {
        LOGE("Unconnect\n");
        return HI_FAILURE;
    }

    HI_CHAR azPkgHead[] = "gesture_msg_head";

    PLUG_COMM_GESTURE_SEND_CFG_S *pstSendCfg = (PLUG_COMM_GESTURE_SEND_CFG_S*)malloc(sizeof(PLUG_COMM_GESTURE_SEND_CFG_S));
    if (HI_NULL == pstSendCfg)
    {
        LOGE("malloc failed, sizeof %d\n", sizeof(PLUG_COMM_GESTURE_SEND_CFG_S));
        return HI_FAILURE;
    }
    HI_CHAR *pazPkgData = (HI_CHAR*)calloc(strlen(pazData) + strlen(azPkgHead) + 4 + 1, 1);
    if(NULL == pazPkgData)
    {
        LOGE("malloc failed, sizeof %d\n", strlen(pazData) + strlen(azPkgHead) + 4 + 1);
        free(pstSendCfg);
        return HI_FAILURE;
    }

    sprintf(pazPkgData, "%s", azPkgHead);
    pazPkgData[strlen(azPkgHead) + 0] = (HI_CHAR)((s32DataLen & 0xff000000) >> 24);
    pazPkgData[strlen(azPkgHead) + 1] = (HI_CHAR)((s32DataLen & 0x00ff0000) >> 16);
    pazPkgData[strlen(azPkgHead) + 2] = (HI_CHAR)((s32DataLen & 0x0000ff00) >> 8);
    pazPkgData[strlen(azPkgHead) + 3] = (HI_CHAR)((s32DataLen & 0x000000ff));
    sprintf(pazPkgData + strlen(azPkgHead) + 4, "%s", pazData);
    pstSendCfg->paData = pazPkgData;
    pstSendCfg->s32Len = strlen(pazData) + strlen(azPkgHead) + 4 + 1;

    printf("%x %x %x %x len %d\n", pazPkgData[strlen(azPkgHead) + 0],
        pazPkgData[strlen(azPkgHead) + 1],
        pazPkgData[strlen(azPkgHead) + 2],
        pazPkgData[strlen(azPkgHead) + 3], s32DataLen);

    pthread_t pt;
    HI_S32 s32Ret = pthread_create(&pt, NULL, PLUG_COMM_GESTURE_SocketSendThread, (void*)pstSendCfg);
    if (0 != s32Ret) {
        LOGE("create PLUG_COMM_GESTURE_SocketSendThread failed\n");
        free(pazPkgData);
        free(pstSendCfg);
        return HI_FAILURE;
    }
    
    return HI_SUCCESS;
}


static HI_S32 PLUG_COMM_GESTURE_SendToClient(HI_CHAR* azMsg, HI_S32 s32DataLen)
{
    HI_APPCOMM_CHECK_POINTER(azMsg, HI_FAILURE);
    return PLUG_COMM_GESTURE_SocketSend(azMsg, s32DataLen);
}


static HI_VOID PLUG_COMM_GESTURE_RecvProc(HI_CHAR *pazRecvBuff)
{
    if (HI_NULL == pazRecvBuff)
    {
        LOGE("Input null\n");
        return ;
    }
    HI_CHAR *pazMsg = HI_NULL;
    LOGI("recv ---------------\n%s\n----------------\n", pazRecvBuff);

    pazMsg = pazRecvBuff + strlen("gesture_msg_head") + 4;
    LOGI("recv msg ---------------\n%s\n----------------\n", pazMsg);

    cJSON *pJsonRoot = HI_NULL;
    cJSON *pJsonPhone2Board = HI_NULL;
    cJSON *pJsonOrder = HI_NULL;
    HI_CHAR azOrder[64] = { 0 };
    pJsonRoot = cJSON_Parse(pazMsg);
    if (HI_NULL == pJsonRoot)
    {
        LOGI("parse cjson (%s) failed\n", pazMsg);
        return ;
    }

    pJsonPhone2Board = cJSON_GetObjectItem(pJsonRoot, "phone2board");
    if (HI_NULL != pJsonPhone2Board)
    {
        pJsonOrder = cJSON_GetObjectItem(pJsonPhone2Board, "order");
        snprintf(azOrder, 64, "%s", pJsonOrder->valuestring);
        LOGI("Order:[%s]\n", azOrder);
        if (0 == strcmp(azOrder, "takephoto"))
        {
            //takephoto
            PLUG_COMM_GESTURE_TakePhoto();
        }
        else if (0 == strcmp(azOrder, "startrecord"))
        {
            //start record
            PLUG_COMM_GESTURE_StartRecord();
        }
        else if (0 == strcmp(azOrder, "stoprecord"))
        {
            //stop record
            PLUG_COMM_GESTURE_StopRecord();
        }
        else if (0 == strcmp(azOrder, "lighton"))
        {
            //lighton
            PLUG_COMM_GESTURE_LightOn();
        }
        else if (0 == strcmp(azOrder, "lightoff"))
        {
            //lightoff
            PLUG_COMM_GESTURE_LightOff();
        }
        else
        {
            LOGE("Unsupported order[%s]\n", azOrder);
        }
    }
    cJSON_Delete(pJsonRoot);
    return ;
}

static void *PLUG_COMM_GESTURE_RecvThread(HI_VOID* args)
{
    pthread_detach(pthread_self());
    HI_S32 s32RecvBuffLen = 1024;
    HI_CHAR azRecvBuff[1024] = { 0 };

    while(HI_FALSE != s_bCommGestureRun && -1 != s_s32ConnectFd)
    {
        HI_S32 s32RecvedLen = 0;
        memset(azRecvBuff, 0, s32RecvBuffLen);
        s32RecvedLen = recv(s_s32ConnectFd, azRecvBuff, s32RecvBuffLen, 0);
        if (s32RecvedLen < 0) {
            LOGE("recv failed, ret = %d\n" , s32RecvedLen);
            continue;
        }

        if (0 == s32RecvedLen) {
            LOGE("client down\n");
            break;
        }

        PLUG_COMM_GESTURE_RecvProc(azRecvBuff);
    }

    pthread_exit(0);
}

static HI_VOID* PLUG_COMM_GESTURE_ServerThread(HI_VOID* args)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_S32 s32SockFd, s32NewConnectFd;
    struct sockaddr_in stServerAddr;
    struct sockaddr_in stClientAddr;
    HI_S32 s32SinSize;

    s32SockFd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == s32SockFd){
        LOGE("socket failed:%d", errno);
        return HI_NULL;
    }

    stServerAddr.sin_family = AF_INET;
    stServerAddr.sin_port=htons(PLUG_COMM_GESTURE_CONNECT_PORT);
    stServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    bzero(&(stServerAddr.sin_zero),8);/*将其他属性置0*/

    if(bind(s32SockFd, (struct sockaddr*)&stServerAddr, sizeof(struct sockaddr)) < 0)
    {
        LOGE("bind error");
        close(s32SockFd);
        return HI_NULL;
    }

    if(listen(s32SockFd, PLUG_COMM_GESTURE_CONNECT_MAXLISTEN) < 0)
    {
        LOGE("listen error");
        close(s32SockFd);
        return HI_NULL;
    }

    while(s_bCommGestureRun){
        s32SinSize=sizeof(struct sockaddr_in);
        
        //accept会阻塞在这里，后续可采用select或SIGIO等方式优化
        s_s32ConnectFd = accept(s32SockFd,(struct sockaddr*)&stClientAddr, &s32SinSize);
        if(-1 == s_s32ConnectFd){
            printf("accept failed");
        } else{
            LOGI("accept client(%d) ip:%s \n", s_s32ConnectFd, inet_ntoa(stClientAddr.sin_addr));
            pthread_t ptRecv;
            s32Ret = pthread_create(&ptRecv, NULL, PLUG_COMM_GESTURE_RecvThread, NULL);
            if(0 != s32Ret)
            {
                LOGE("create PLUG_COMM_GESTURE_RecvThread failed\n");
            }
        }
    }

    close(s32SockFd);

    return HI_NULL;
}

static HI_S32 PLUG_COMM_GESTURE_StartServer(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    pthread_t ptRecv;
    s_bCommGestureRun = HI_TRUE;
    s32Ret = pthread_create(&ptRecv, NULL, PLUG_COMM_GESTURE_ServerThread, NULL);
    if(0 != s32Ret)
    {
        s_bCommGestureRun = HI_FALSE;
        LOGE("create PLUG_COMM_GESTURE_RecvThread failed\n");
    }
    return HI_SUCCESS;
}

static HI_S32 PLUG_COMM_GESTURE_StopServer(HI_VOID)
{
    s_bCommGestureRun = HI_FALSE;
}

static HI_S32 PLUG_COMM_GESTURE_UDPPost(HI_CHAR *pazIP, HI_CHAR *pazMsg)
{
    HI_S32 s32UdpFd;
    if((s32UdpFd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("socket fail\n");
        return HI_FAILURE;
    }

    HI_S32 s32Optval = 1;
    setsockopt(s32UdpFd, SOL_SOCKET, SO_BROADCAST | SO_REUSEADDR, &s32Optval, sizeof(HI_S32));
    struct sockaddr_in stDstAddr;
    memset(&stDstAddr, 0, sizeof(struct sockaddr_in));
    stDstAddr.sin_family = AF_INET;
    stDstAddr.sin_addr.s_addr = inet_addr(pazIP);
    stDstAddr.sin_port = htons(PLUG_COMM_GESTURE_UDP_PORT);

    int sendBytes;
    if((sendBytes = sendto(s32UdpFd, pazMsg, strlen(pazMsg), 0,
            (struct sockaddr *)&stDstAddr, sizeof(struct sockaddr))) == -1)
    {
        printf("sendto fail, errno=%d\n", errno);
        return HI_FAILURE;
    }

    printf("msg=%s, msgLen=%d, sendBytes=%d\n", pazMsg, strlen(pazMsg), sendBytes);
    close(s32UdpFd);
    return HI_SUCCESS;
}

static HI_S32 PLUG_COMM_GESTURE_EventProc(PLUG_DISCOVERY_EVENT_S *pstEvent)
{
    HI_APPCOMM_CHECK_POINTER(pstEvent, HI_SUCCESS);
    HI_S32 s32Ret = HI_SUCCESS;

    switch(pstEvent->enType)
    {
        case PLUG_DISCOVERY_EVENT_NEWBROADCAST:
        {
            HI_CHAR azIP[32] = { 0 };
            HI_CHAR *pazJson = HI_NULL;
            LOGI("get client ip [%s]\n", pstEvent->stCfg.azIP);
            PLUG_COMM_GetLoaclIP(azIP);
            cJSON *pJsonRoot = cJSON_CreateObject();
            if (HI_NULL == pJsonRoot)
            {
                LOGE("cJSON_CreateObject fail!\n");
                return HI_FAILURE;
            }
            cJSON_AddStringToObject(pJsonRoot, "GESTURE_SERVER", azIP);

            //snprintf(azMsg, sizeof(azMsg), "GESTURE_SERVER:%s", azIP);
            pazJson = cJSON_Print(pJsonRoot);
            PLUG_COMM_GESTURE_UDPPost(pstEvent->stCfg.azIP, pazJson);
            free(pazJson);
            break;
        }
        default:
        {
            LOGI("unknown event type[%d]\n", pstEvent->enType);
            return HI_FAILURE;
        }
    }

    return HI_SUCCESS;
}

HI_S32 PLUG_COMM_GESTURE_Init(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    s32Ret = PLUG_COMM_GESTURE_StartServer();
    HI_APPCOMM_CHECK_EXPR((HI_SUCCESS == s32Ret), HI_FAILURE);

    /* 启动动态IP发现 */
    s32Ret = PLUG_DISCOVERY_Init();
    HI_APPCOMM_CHECK_EXPR((HI_SUCCESS == s32Ret), HI_FAILURE);
    s32Ret = PLUG_DISCOVERY_Register(PLUG_COMM_GESTURE_EventProc);
    HI_APPCOMM_CHECK_EXPR((HI_SUCCESS == s32Ret), HI_FAILURE);

    return HI_SUCCESS;
}

HI_S32 PLUG_COMM_GESTURE_SendMsg(HI_CHAR *pazMsg, HI_S32 s32DataLen)
{
    return PLUG_COMM_GESTURE_SendToClient(pazMsg, s32DataLen);
}

HI_S32 PLUG_COMM_GESTURE_Deinit(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    /* 停止动态IP发现 */
    s32Ret = PLUG_DISCOVERY_UnRegister(PLUG_COMM_GESTURE_EventProc);
    HI_APPCOMM_CHECK_EXPR((HI_SUCCESS == s32Ret), HI_FAILURE);
    s32Ret = PLUG_DISCOVERY_Deinit();
    HI_APPCOMM_CHECK_EXPR_WITHOUT_RETURN((HI_SUCCESS == s32Ret), "PLUG_DISCOVERY_Deinit failed");

    PLUG_COMM_GESTURE_StopServer();
    HI_APPCOMM_CHECK_EXPR_WITHOUT_RETURN((HI_SUCCESS == s32Ret), "PLUG_COMM_GESTURE_StopServer failed");

    return HI_SUCCESS;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */

